/*
* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd. 
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "include/client_factory.h"

#include <thread>
#include <unistd.h>

#include "include/i_client_cb.h"
#include "include/client_factory.h"
#include "engine/include/service_adapter.h"
#include "platform/time/include/time.h"
#include "protocol/retcode_inner/infer_retcode_inner.h"
#include "utils/constants/constants.h"
#include "utils/log/infer_log.h"

namespace OHOS {
namespace AI_STD {
std::mutex ClientFactory::sessionIdMutex_;
namespace {
    const int MAXIMUM_NUMBER_OF_SESSION = 65535;
}
std::mutex ClientFactory::instance_mutex_;
ClientFactory *ClientFactory::instance_ = nullptr;

ClientFactory::ClientFactory() : clientId_(INVALID_CLIENT_ID), sessionId_(INFER_SESSION_ID_BEGIN)
{
}

ClientFactory::~ClientFactory() = default;


ClientFactory *ClientFactory::GetInstance()
{
    CHK_RET(instance_ != nullptr, instance_);
    std::lock_guard<std::mutex> lock(instance_mutex_);
    CHK_RET(instance_ != nullptr, instance_);
    AI_NEW_INSTANCE(instance_, ClientFactory);
    return instance_;
}

void ClientFactory::ReleaseInstance()
{
    std::lock_guard<std::mutex> lock(instance_mutex_);
    AI_DELETE_INSTANCE(instance_);
}

int ClientFactory::ClientInit(const ConfigInfo &configInfo, ClientInfo &clientInfo,
    const AlgorithmInfo &algorithmInfo, IServiceDeadCb *cb)
{
    HILOGI("[ClientFactory]Begin to call ClientInit.");
    if (clientInfo.sessionId != INVALID_SESSION_ID) {
        HILOGW("[ClientFactory]Session id is valid, does not need to do Init.");
        // Valid sessionId tells it has been already INIT successfully before
        return RETCODE_SUCCESS;
    }

    clientInfo.sessionId = GenerateSessionId();
    int retCode = AddSessionInfo(clientInfo, algorithmInfo);
    if (retCode != RETCODE_SUCCESS) {
        HILOGE("[ClientFactory][sessionId:%d]Fail to Add session info.", clientInfo.sessionId);
        return RETCODE_FAILURE;
    }

    clientId_ = GenerateClient();
    if (clientId_ == INVALID_CLIENT_ID) {
        HILOGE("[ClientFactory][sessionId:%d]Fail to generate client.", clientInfo.sessionId);
        return RETCODE_FAILURE;
    }
    clientInfo.clientId = clientId_;
    clientInfo.clientUid = getuid();
    
    return RETCODE_SUCCESS;
}

int ClientFactory::ClientPrepare(const ClientInfo &clientInfo, const AlgorithmInfo &algorithmInfo,
    const DataInfo &inputInfo, DataInfo &outputInfo, IClientCb *cb)
{
    HILOGI("[ClientFactory]Begin to call ClientPrepare.");
    if (clientInfo.sessionId == INVALID_SESSION_ID) {
        HILOGI("[ClientFactory][clientId:%d, sessionId:%d]SessionId is invalid,"\
            "please call aie clientInit firstly.",
            clientId_, clientInfo.sessionId);
        return RETCODE_SERVER_NOT_INIT;
    }
    int retCode = RETCODE_SUCCESS;
    if (algorithmInfo.isAsync) {
        HILOGE("[ClientFactory][isAsync:%d]Not support Async inference now.",
                algorithmInfo.isAsync);
        return RETCODE_INVALID_PARAM;
    }

    ServiceAdapter *adapter = FindAdapter(clientInfo.clientId);
    if (adapter == nullptr) {
        HILOGE("[AdapterWrapper]No adapter found for client[%d].", clientInfo.clientId);
        return RETCODE_NO_CLIENT_FOUND;
    }

    AdapterWrapper adapterGuard(adapter);
    long long transactionId = adapter->GetTransactionId(clientInfo.sessionId);
    retCode = adapter->LoadAlgorithm(transactionId, algorithmInfo, inputInfo, outputInfo);
    if (retCode != RETCODE_SUCCESS) {
        HILOGE("[AdapterWrapper][transactionId:%lld]Failed to load algorithm, retCode[%d], aid[%s].",
            transactionId, retCode, algorithmInfo.algorithmName.c_str());
        return retCode;
    }

    return retCode;
}

int ClientFactory::ClientAsyncProcess(const ClientInfo &clientInfo, const AlgorithmInfo &algorithmInfo,
    const DataInfo &inputInfo)
{
    HILOGI("[ClientFactory]Begin to call ClientAsyncProcess.");
    if (clientInfo.sessionId == INVALID_SESSION_ID) {
        HILOGE("[ClientFactory][clientId:%d, sessionId:%d] sessionId is invalid, please call Init firstly.",
            clientId_, clientInfo.sessionId);
        return RETCODE_SERVER_NOT_INIT;
    }
    if (!algorithmInfo.isAsync) {
        HILOGE("[ClientFactory]algorithm is synchronous, but async process is called.");
        return RETCODE_INVALID_PARAM;
    }

    ServiceAdapter *adapter = FindAdapter(clientInfo.clientId);
    if (adapter == nullptr) {
        HILOGE("[AdapterWrapper]No adapter found for client[%d].", clientInfo.clientId);
        return RETCODE_NO_CLIENT_FOUND;
    }
    AdapterWrapper adapterGuard(adapter);

    return adapter->AsyncExecute(clientInfo, algorithmInfo, inputInfo);
}

int ClientFactory::ClientDestroy(ClientInfo &clientInfo)
{
    HILOGI("[ClientFactory]Begin to call ClientDestroy.");
    if (clientInfo.sessionId == INVALID_SESSION_ID) {
        HILOGW("[ClientFactory][clientId:%d, sessionId:%d] sessionId is invalid value, not need destroy.",
            clientId_, clientInfo.sessionId);
        // invalid session id tells it has been already destroyed before, it is still successfully destroy
        return RETCODE_FAILURE;
    }
    int retCode = EraseSessionInfo(clientInfo.sessionId);
    if (retCode != RETCODE_SUCCESS) {
        return retCode;
    }

    retCode = RemoveAdapter(&clientInfo);
    if (retCode != RETCODE_SUCCESS) {
        HILOGE("[AdapterWrapper]No adapter found for client[%d].", clientInfo.clientId);
        return retCode;
    }

    clientInfo.sessionId = INVALID_SESSION_ID;
    clientInfo.clientId = INVALID_CLIENT_ID;

    clientId_ = INVALID_SESSION_ID;

    HILOGD("[ClientFactory][clientId:%d, sessionId:%d]End to call ClientDestroy, result code[%d].",
        clientId_, clientInfo.sessionId, retCode);
    return retCode;
}

int ClientFactory::ClientSetOption(const ClientInfo &clientInfo, int optionType, const DataInfo &inputInfo)
{
    HILOGI("[ClientFactory]Begin to call ClientSetOption.");
    ServiceAdapter *adapter = FindAdapter(clientInfo.clientId);
    if (adapter == nullptr) {
        HILOGE("[AdapterWrapper]No adapter found for client[%d].", clientInfo.clientId);
        return RETCODE_NO_CLIENT_FOUND;
    }

    AdapterWrapper adapterGuard(adapter);
    long long transactionId = adapter->GetTransactionId(clientInfo.sessionId);
    return adapter->SetOption(transactionId, optionType, inputInfo);
}

int ClientFactory::ClientGetOption(const ClientInfo &clientInfo, int optionType,
    const DataInfo &inputInfo, DataInfo &outputInfo)
{
    HILOGI("[ClientFactory]Begin to call ClientGetOption.");
    ServiceAdapter *adapter = FindAdapter(clientInfo.clientId);
    if (adapter == nullptr) {
        HILOGE("[AdapterWrapper]No adapter found for client[%d].", clientInfo.clientId);
        return RETCODE_NO_CLIENT_FOUND;
    }

    AdapterWrapper adapterGuard(adapter);
    long long transactionId = adapter->GetTransactionId(clientInfo.sessionId);
    return adapter->GetOption(transactionId, optionType, inputInfo, outputInfo);
}

int ClientFactory::ClientSyncProcess(const ClientInfo &clientInfo, const AlgorithmInfo &algorithmInfo,
    const DataInfo &inputInfo, DataInfo &outputInfo)
{
    HILOGI("[ClientFactory]Begin to call ClientSyncProcess.");
    if (clientInfo.sessionId == INVALID_SESSION_ID) {
        HILOGE("[ClientFactory]SessionId is invalid, please call Init firstly.");
        return RETCODE_SERVER_NOT_INIT;
    }
    if (algorithmInfo.isAsync) {
        HILOGE("[ClientFactory]algorithm is asynchronous, but sync process is called.");
        return RETCODE_INVALID_PARAM;
    }

    ServiceAdapter *adapter = FindAdapter(clientInfo.clientId);
    if (adapter == nullptr) {
        HILOGE("[AdapterWrapper]No adapter found for client[%d].", clientInfo.clientId);
        return RETCODE_NO_CLIENT_FOUND;
    }

    AdapterWrapper adapterGuard(adapter);
    return adapter->SyncExecute(clientInfo, algorithmInfo, inputInfo, outputInfo);
}

int ClientFactory::ClientRelease(const ClientInfo &clientInfo, const AlgorithmInfo &algorithmInfo,
    const DataInfo &inputInfo)
{
    HILOGI("[ClientFactory]Begin to call ClientRelease.");
    if (clientInfo.sessionId == INVALID_SESSION_ID) {
        HILOGI("[ClientFactory][clientId:%d, sessionId:%d] sessionId is invalid value, not need release.",
            clientId_, clientInfo.sessionId);
        return RETCODE_SUCCESS;
    }

    ServiceAdapter *adapter = FindAdapter(clientInfo.clientId);
    if (adapter == nullptr) {
        HILOGE("[ClientFactory]No adapter found for client[%d].", clientInfo.clientId);
        return RETCODE_NO_CLIENT_FOUND;
    }

    AdapterWrapper adapterGuard(adapter);
    long long transactionId = adapter->GetTransactionId(clientInfo.sessionId);

    return adapter->UnloadAlgorithm(transactionId, inputInfo);
}

void ClientFactory::SetClientId(int clientId)
{
    clientId_ = clientId;
}

int ClientFactory::GetClientId() const
{
    return clientId_;
}

void ClientFactory::SetServerUid(const uid_t clientId)
{
    serverUid_ = clientId;
}

uid_t ClientFactory::GetServerUid() const
{
    return serverUid_;
}

int ClientFactory::GenerateSessionId()
{
    std::lock_guard<std::mutex> lock(sessionIdMutex_);
    if (sessionInfos_.size() > MAXIMUM_NUMBER_OF_SESSION) {
        HILOGE("[ClientFactory]Session id exceed maximum numbers.");
        return INVALID_SESSION_ID;
    }
    do {
        ++sessionId_;
        if (sessionId_ < INFER_SESSION_ID_BEGIN) {
            // session id may come to the maximum value and turn to negative value
            HILOGI("[ClientFactory]SessionId reaches max int value, now reset it to init value(%d).",
                INFER_SESSION_ID_BEGIN);
            sessionId_ = INFER_SESSION_ID_BEGIN;
        }
    } while (sessionInfos_.find(sessionId_) != sessionInfos_.end());
    return sessionId_;
}

int ClientFactory::AddSessionInfo(const ClientInfo &clientInfo, const AlgorithmInfo &algorithmInfo)
{
    std::lock_guard<std::mutex> lock(sessionIdMutex_);
    std::pair<std::map<int, std::string>::iterator, bool> ret;
    ret = sessionInfos_.insert(std::make_pair(clientInfo.sessionId, algorithmInfo.algorithmName));
    if (ret.second == false) {
        HILOGE("[ClientFactory][sessionId:%d]sessionInfos insert failed.", clientInfo.sessionId);
        return RETCODE_FAILURE;
    }
    return RETCODE_SUCCESS;
}

int ClientFactory::EraseSessionInfo(int sessionId)
{
    std::lock_guard<std::mutex> lock(sessionIdMutex_);
    if (!sessionInfos_.erase(sessionId)) {
        HILOGW("[ClientFactory][sessionId:%d]session id does not exist.", sessionId);
        return RETCODE_FAILURE;
    }
    return RETCODE_SUCCESS;
}

int ClientFactory::GetSessionInfo(int sessionId, std::string &algorithmName)
{
    std::lock_guard<std::mutex> lock(sessionIdMutex_);
    SessionInfos::iterator iter = sessionInfos_.find(sessionId);
    CHK_RET(iter == sessionInfos_.end(), RETCODE_EMPTY_MAP);
    algorithmName = iter->second;
    return RETCODE_SUCCESS;
}

void ClientFactory::ResetClient()
{
    clientId_ = INVALID_CLIENT_ID;
    sessionId_ = INVALID_SESSION_ID;
    serverUid_ = INVALID_UID;
    clientId_ = INVALID_UID;
}
} // namespace AI_STD
} // namespace OHOS
